﻿using NetJavap.AttributeInfos;
using NetJavap.ConstantInfos;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NetJavap
{
    public static class ArrayBuilder
    {
        private static Dictionary<String, Type> _attributeTypes = new Dictionary<string, Type>();
        private static Dictionary<byte, Type> _constantPoolTypes = new Dictionary<byte, Type>();

        public static AttributeInfo[] BuildAttributes(BinaryReader reader, JavaClass cls, UInt16 attrCount, ClassMember defineMember = null)
        {
            List<AttributeInfo> attrs = new List<AttributeInfo>();
            for (int i = 0; i < attrCount; i++)
            {
                UInt16 attrNameId = reader.ReadUInt16BE();
                reader.BaseStream.Position -= 2;
                string attrName = (cls.ConstantPool[attrNameId] as ConstantUtf8Info).Value;
                AttributeInfo info = Activator.CreateInstance(_attributeTypes[attrName], reader, cls, defineMember) as AttributeInfo;
                info.DefineMember = defineMember;
                attrs.Add(info);
            }
            return attrs.ToArray();
        }
        public static Dictionary<UInt16, ConstantInfo> BuildConstants(BinaryReader reader, JavaClass cls, UInt16 consCount)
        {
            Dictionary<UInt16, ConstantInfo> cons = new Dictionary<UInt16, ConstantInfo>();
            for (UInt16 i = 1; i < consCount; i++)
            {
                Byte consType = reader.ReadByte();
                reader.BaseStream.Position--;
                ConstantInfo info = Activator.CreateInstance(_constantPoolTypes[consType], reader, cls) as ConstantInfo;
                cons[i] = info;
                if (info is ConstantDoubleInfo || info is ConstantLongInfo)
                {
                    i++;
                }
            }
            return cons;
        }
        public static Dictionary<UInt32, JOpcode> BuildOpCodes(byte[] code, JavaClass cls)
        {
            MemoryStream ms = new MemoryStream(code);
            BinaryReader reader = new BinaryReader(ms, Encoding.BigEndianUnicode);
            Dictionary<UInt32, JOpcode> opcodeDir = new Dictionary<UInt32, JOpcode>();
            UInt32 currentPos = 0;
            while (true)
            {
                JOpcode opcode = JOpcodeBuilder.ReadOne(reader, cls);
                if (opcode == null)
                {
                    break;
                }
                opcodeDir[currentPos] = opcode;
                currentPos += (UInt32)opcode.OtherBytes.Length + 1;
            }
            return opcodeDir;
        }
        public static T[] BuildArray<T>(Func<T> buildFunc, int count)
        {
            List<T> objs = new List<T>();
            for (int i = 0; i < count; i++)
            {
                objs.Add(buildFunc());
            }
            return objs.ToArray();
        }
        public static void AnalyseDescriptor(String des, out TypeInfo[] pars, out TypeInfo retType)
        {
            Stack<TypeInfo> parStack = new Stack<TypeInfo>();
            int arrLen = 0;
            String typeName = "";
            for (int i = 0; i < des.Length; i++)
            {
                char currentChar = des[i];
                switch (currentChar)
                {
                    case '(':
                    case ')':
                        continue;
                    case '[':
                        arrLen++;
                        continue;
                    case 'L':
                        i++;
                        while (des[i] != ';')
                        {
                            typeName += des[i];
                            i++;
                        }
                        break;
                    default:
                        typeName = currentChar + "";
                        break;
                }
                TypeInfo parInfo = new TypeInfo(typeName, arrLen);
                parStack.Push(parInfo);
                typeName = "";
                arrLen = 0;
            }
            retType = parStack.Pop();
            pars = parStack.ToArray();
        }
        public static TypeInfo CreateTypeInfo(String typeIdenName)
        {
            int arrLen = 0;
            int i = 0;
            String typeName = "";
            while (typeIdenName[i] == '[')
            {
                arrLen++;
                i++;
            }
            if (typeIdenName[i] == 'L')
            {
                i++;
                while (typeIdenName[i] != ';' && i < typeIdenName.Length)
                {
                    typeName += typeIdenName[i];
                    i++;
                }
                TypeInfo parInfo = new TypeInfo(typeName, arrLen);
                return parInfo;
            }
            else
            {
                typeName = typeIdenName.Substring(i, typeIdenName.Length - i);
                TypeInfo parInfo = new TypeInfo(typeName, arrLen);
                return parInfo;
            }

        }
        static ArrayBuilder()
        {
            _attributeTypes["Code"] = typeof(CodeAttributeInfo);
            _attributeTypes["ConstantValue"] = typeof(ConstantValueAttributeInfo);
            _attributeTypes["Exceptions"] = typeof(ExceptionsAttributeInfo);
            _attributeTypes["InnerClasses"] = typeof(InnerClassesAttributeInfo);
            _attributeTypes["LineNumberTable"] = typeof(LineNumberTableAttribbuteInfo);
            _attributeTypes["LocalVariableTable"] = typeof(LocalVariableTableAttributeInfo);
            _attributeTypes["SourceFile"] = typeof(SourceFileAttributeInfo);
            _attributeTypes["StackMapTable"] = typeof(StackMapTableAttributeInfo);
            _attributeTypes["Signature"] = typeof(SignatureAttributeInfo);

            _attributeTypes["Deprecated"] = typeof(CustomAttributeInfo);
            _attributeTypes["RuntimeVisibleAnnotations"] = typeof(CustomAttributeInfo);
            _attributeTypes["LocalVariableTypeTable"] = typeof(CustomAttributeInfo);

            _constantPoolTypes[1] = typeof(ConstantUtf8Info);
            _constantPoolTypes[3] = typeof(ConstantIntegerInfo);
            _constantPoolTypes[4] = typeof(ConstantFloatInfo);
            _constantPoolTypes[5] = typeof(ConstantLongInfo);
            _constantPoolTypes[6] = typeof(ConstantDoubleInfo);
            _constantPoolTypes[7] = typeof(ConstantClassInfo);
            _constantPoolTypes[8] = typeof(ConstantStringInfo);
            _constantPoolTypes[9] = typeof(ConstantFieldRefInfo);
            _constantPoolTypes[10] = typeof(ConstantMethodRefInfo);
            _constantPoolTypes[11] = typeof(ConstantInterfaceMethodRefInfo);
            _constantPoolTypes[12] = typeof(ConstantNameAndTypeInfo);
        }
    }
}
